#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>

#include "node.h"
#include "symbol.h"
#include "libblogc/list.h"

/* arglist is owned by the node */
node * node_alloc(nodetype type, void * childlist, char * fname, int lnum)
{
  node * node1;
  
  node1 = (node *)malloc(sizeof(*node1));
  node1->type = type;
  node1->fname = fname;
  node1->lnum = lnum;
  node1->childlist = childlist;
  node1->valtype = NULL;
  node1->genname = NULL;
  
  return node1;
}

/* any pointers in leaf are owned by node */
node * nodeleaf_alloc(nodetype type, nodeleaf leaf, char * fname, int lnum)
{
  node * node1;
  
  node1 = (node *)malloc(sizeof(*node1));
  node1->type = type;
  node1->fname = fname;
  node1->lnum = lnum;
  node1->childlist = NULL;
  node1->leaf = leaf;
  node1->valtype = NULL;
  node1->genname = NULL;

  return node1;
}

void node_print(node * node1)
{
  switch(node1->type)
  {
  case NODETYPE_NULL:
    printf("null");
    break;
    
  case NODETYPE_INTEGER:
    printf("(INT %d ", node1->leaf.intval);
    break;
    
  case NODETYPE_REAL:
    printf("(REAL %f ", node1->leaf.doubleval);
    break;

  case NODETYPE_STRING:
    printf("(STR %s ", node1->leaf.pchar);
    break;
    
  case NODETYPE_IDENTIFIER:
    printf("(ID %s ", node1->leaf.pchar);
    break;

  case NODETYPE_SYMBOL:
    printf("(SYM %s ", node1->leaf.psym->name);
    break;

  /* internal nodes */
  case NODETYPE_RkVECTOR:
    printf("(RkVector ");
    break;
    
  case NODETYPE_FN_CALL:
    printf("(FN_CALL ");
    break;
    
  case NODETYPE_AND:
    printf("(& ");
    break;

  case NODETYPE_OR:
    printf("(| ");
    break;

  case NODETYPE_EQUAL:
    printf("(= ");
    break;
    
  case NODETYPE_NOT_EQUAL:
    printf("(!= ");
    break;
    
  case NODETYPE_CPD_CALL:
    printf("(CPD_CALL ");
    break;
  case NODETYPE_IF:                                  /* if statement */
    printf("(IF ");
    break;
  case NODETYPE_DET_DEP:                /* deterministic dependency */
    printf("(DEP= ");
    break;
  case NODETYPE_STO_DEP:                   /* stochastic dependency */
    printf("(DEP~ ");
    break;
  case NODETYPE_SET_IMP:                 /* implicit set of objects */
    printf("(SET_IMP ");
    break;
  case NODETYPE_SET_EXP:                 /* explicit set of objects */
    printf("(SET_EXP ");
    break;
  case NODETYPE_SET_CARD:                    /* cardinality of a set */
    printf("(SET_CARD ");
    break;
  case NODETYPE_LIST:
    printf("(");
    break;

  default:
    printf("(unknown nodetype");
    break;
  }
  
  if (node1->genname)
    printf("[name=%s] ", node1->genname);
  
  if (node1->valtype)
    printf("[type=%s] ", node1->valtype->name);

  if (NULL != node1->childlist)
  {
    void * iter;
    node * node2;
    for(iter = list_next(node1->childlist); NULL != iter;
        iter = list_next(iter))
    {
      node2 = (node *)list_item(iter);
      node_print(node2);
      if (NULL != list_next(iter))
        printf(" ");
    }
  }
  printf(")");
}

/* get an Real from a node, return an error if it's of the wrong type
 * a NATURAL is also acceptable */
double node_get_real(node * node1)
{
  if (NODETYPE_REAL == node1->type)
  {
    return node1->leaf.doubleval;
  }
  else if (NODETYPE_INTEGER == node1->type)
  {
    return (double) node1->leaf.intval;
  }
  else
  {
    fprintf(stderr, "Error: expecting a Real\n");
    exit(1);
  }
}

/* get an RkVector from a node, return an error if it's of the wrong type */
vector * node_get_rkvector(node * node1)
{
  vector * vec;
  list_t iter;
  
  if (NODETYPE_RkVECTOR != node1->type)
  {
    fprintf(stderr, "Error: expecting an RkVECTOR\n");
    exit(1);
  }

  vec = vector_alloc(list_count(node1->childlist));
  
  for (iter = list_next(node1->childlist); iter; iter = list_next(iter))
  {
    vector_append(vec, node_get_real((node *)list_item(iter)));
  }

  return vec;
}

/* raise an error on the specified node and exit */
void * node_error(node * node1, char * format, ... )
{
  char buffer[1000];
  va_list args;

  va_start(args, format);  
  vsnprintf(buffer, 1000, format, args);
  fprintf(stderr, "%s:%d: error: %s\n", node1->fname, node1->lnum, buffer);
  va_end(args);
  
  exit(1);
}
